#include <cstdio>
#include <cassert>
#include <cstring>

#include <stddef.h>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "com_facesdk_FDFaceProcessor.h"
#include "is_facedetect.h"

#include <android/log.h>
#define  LOG_TAG    "gdface:"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

static jmethodID con_mid=NULL;
static jmethodID set_face_num=NULL;
static jmethodID set_face_position=NULL;
static jmethodID set_eye_info=NULL;
static bool init_status=false;

static int init_jclass_jmethod(JNIEnv *env)
{

    jclass face_info_class=env->FindClass("com/facesdk/FaceInfo");
    assert(face_info_class!=NULL);
    if(face_info_class==NULL){
	LOGE("internal error:FaceInfo class couldn't be found.");
        return -1;
    }
    con_mid=env->GetMethodID(face_info_class,"<init>","()V");
    assert(con_mid!=NULL);
    if(con_mid==NULL){
	LOGE("internal error:FaceInfo's construct method couldn't be fetched.");
        return -1;
    }

    set_face_num=env->GetMethodID(face_info_class,"setFace_num","(I)V");
    assert(set_face_num!=NULL);
    if(set_face_num==NULL){
	LOGE("internal error:FaceInfo's setFace_num method couldn't be fetched.");
        return -1;
    }

    set_face_position=env->GetMethodID(face_info_class,"addFace_pos","(IIII)V");
    assert(set_face_position!=NULL);
    if(set_face_position==NULL){
	LOGE("internal error:FaceInfo's addFace_pos method couldn't be fetched.");
        return -1;
    }

    set_eye_info=env->GetMethodID(face_info_class,"addEye_info","(IIII)V");
    assert(set_eye_info!=NULL);
    if(set_eye_info==NULL){
	LOGE("internal error:FaceInfo's addEye_info method couldn't be fetched.");
        return -1;
    }
    return 0;
}

/*
 * Class:     com_facesdk_FDFaceProcessor
 * Method:    FDInit
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_facesdk_FDFaceProcessor_FDInit
  (JNIEnv *env, jclass) {
    FDDLLInfo fddllinfo[10];
    LOGD("initializing FD_Face......");
    fflush(stdout);
    if(init_jclass_jmethod(env)!=0||/*face_detect.*/FDInit(2<<8|1,fddllinfo)!=FD_OK){
        LOGE("init FD Face failed.");
        return -1;
    }
    init_status=true;
    LOGD("init FD Face completed.");
    fflush(stdout);
}

/*
 * Class:     com_facesdk_FDFaceProcessor
 * Method:    FDUinit
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_facesdk_FDFaceProcessor_FDUinit
  (JNIEnv *, jobject) {
    /*face_detect.*/FDUninit();
}

/*
 * Class:     com_facesdk_FDFaceProcessor
 * Method:    FDFace
 * Signature: ([BIIDIIII)Lcom/facesdk/FaceInfo;
 */
JNIEXPORT jobject JNICALL Java_com_facesdk_FDFaceProcessor_FDFace
  (JNIEnv *env, jclass, jbyteArray image_data, jint width, jint height, jdouble fscale,
   jint minw,jint minh,jint maxw,jint maxh) {
    if(!init_status||image_data==NULL) {
	LOGE("init_status false OR image_data is null, return NULL!");
	return NULL;
    }        

    int face_num;
    FacePos face_pos_arr[50];
    //EyeInfo eye_info;
    //FSize min_val,max_val;

    jbyte *data=env->GetByteArrayElements(image_data,NULL);
#if 0
    unsigned char *data=(unsigned char *)malloc(width*height*sizeof(unsigned char));
    if(data==NULL){
        fprintf(stderr,"malloc failed!\n");
        return NULL;
    }
    int len=width*height;
    for(int i=0;i!=len;i++)
        data[i]=(unsigned char)tmp_data[i];
    //RGB2Gray(tmp_data,data,width,height);

#endif

    FSize min,max;
    min.height=minw;
    min.width=minh;
    max.height=height;
    max.width=width;

    int ret=/*face_detect.*/FDFace((unsigned char *)data,width,height,
                          &face_num,face_pos_arr,
                          NULL,fscale,
                          &min,&max);

    env->ReleaseByteArrayElements(image_data,data,JNI_ABORT);


#ifndef NDEBUG
    LOGD("ret=%d\n",ret);
#endif

    //free(data);

    if(ret!=FD_OK){
	LOGE("FD_Face() failed return %d.",ret);
        return NULL;
    }

    if(face_num==0) {
	LOGE("face num = 0, return NULL");
        return NULL;
    }
    LOGD("face num = %d",face_num);
    jclass face_info_class=env->FindClass("com/facesdk/FaceInfo");
    if(face_info_class==NULL)
	LOGE("internal error:Face Info class couldn't be found.");
    jobject face_info_obj=env->NewObject(face_info_class,con_mid);
    if(face_info_obj==NULL){
	LOGE("face_info_obj is null");
    }
    //env->CallObjectMethod(face_info_obj,set_face_num,face_num);
    env->CallVoidMethod(face_info_obj,set_face_num,face_num);

#ifndef NDEBUG
    LOGD("face number: c++:%d",face_num);
#endif
    for(int i=0;i<face_num;i++){
        env->CallVoidMethod(face_info_obj,set_face_position,face_pos_arr[i].left,face_pos_arr[i].top,
                              face_pos_arr[i].width,face_pos_arr[i].height);

#ifndef NDEBUG
	LOGD("face_pos: c++:%d %d %d %d.\n",face_pos_arr[i].left,face_pos_arr[i].top,
               face_pos_arr[i].width,face_pos_arr[i].height);
#endif

#if 0
        env->CallVoidMethod(face_info_obj,set_eye_info,eye_info.leftx,eye_info.lefty,eye_info.rightx,eye_info.righty);


        printf("eye_info: c++: %d %d %d %d.\n",eye_info.leftx,eye_info.lefty,eye_info.rightx,eye_info.righty);
#endif

#if 0
        jmethodID sfmis=env->GetMethodID(face_info_class,"setFace_min_size","(II)V");
        env->CallObjectMethod(face_info_obj,sfmis,min_val.width,min_val.height);

        jmethodID sfmas=env->GetMethodID(face_info_class,"setFace_max_size","(II)V");
        env->CallObjectMethod(face_info_obj,sfmas,max_val.width,max_val.height);
#endif
    }

    return face_info_obj;
}

/*
 * Class:     com_facesdk_FDFaceProcessor
 * Method:    RGB2Gray
 * Signature: ([III)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_facesdk_FDFaceProcessor_RGB2Gray
  (JNIEnv *env, jclass, jintArray arr, jint w, jint h) {
    
    int len=w*h;
    LOGD("RGB2Gray: len is %d.",len);
    jint *rgb_data=env->GetIntArrayElements(arr,NULL);

    cv::Mat src(h, w, CV_8UC3);
    cv::Mat dst(h,w,CV_8U);

    int count=0;
    unsigned char *psrc=src.data;
    for(int i=0;i<len;i++){
        psrc[count++]=(rgb_data[i]&0x000000FF);
        psrc[count++]=((rgb_data[i]&0x0000FF00)>>8);
        psrc[count++]=((rgb_data[i]&0x00FF0000)>>16);

    }
    cv::cvtColor(src,dst,CV_RGB2GRAY);

    env->ReleaseIntArrayElements(arr,rgb_data,JNI_ABORT);

    jbyteArray jb_arr=env->NewByteArray(sizeof(char)*len);
    env->SetByteArrayRegion(jb_arr,0,len,(jbyte *)dst.data);

    return jb_arr;
   
}


